package cn.zz.comm.tool;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import cn.hutool.core.lang.UUID;
import cn.zz.comm.util.JsonUtil;
import cn.zz.comm.util.StrUtil;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zhongq
 * @since 2024/5/17
 */
@Slf4j
public class JwtTool {
	private static final long DEFAULT_EXPIRE_SECONDS = 24 * 60 * 60 * 1000L;

	private final Long expire;
	private final PublicKey publicKey;
	private final PrivateKey privateKey;
	private final JwtParser jwtParser;


	public JwtTool(Long expireSeconds, String priKey, String pubKey) {
		this.expire = Optional.ofNullable(expireSeconds).orElse(DEFAULT_EXPIRE_SECONDS) * 1000;
		this.publicKey = getPublicKeyFromBase64EncodedStr(pubKey);
		this.privateKey = Optional.ofNullable(priKey).map(JwtTool::getPrivateKeyFromBase64EncodedStr).orElse(null);
		this.jwtParser = Jwts.parser().verifyWith(publicKey).build();
	}


	/**
	 * generate jwt token.
	 *
	 * @return token
	 */
	public String geneToken(Integer uid) {
		final String jwtId = UUID.randomUUID().toString(true);
		final JwtBuilder builder = Jwts.builder().id(jwtId).claim("uid", uid);
		builder.expiration(new Date(System.currentTimeMillis() + expire));
		return builder.signWith(privateKey, Jwts.SIG.RS256).compact();
	}

	/**
	 * parse token(jwt)
	 *
	 * @param token token to be parsed
	 * @return parameters, null if parse failed
	 */
	public Map<String, Object> parseToken(String token) {
		try {
			return jwtParser.parseSignedClaims(token).getPayload();
		} catch (Exception e) {
			log.info("process jwt ->{}<- failed", token);
		}
		return new HashMap<>();
	}

	/**
	 * get jti
	 *
	 * @param token token
	 * @return jti
	 */
	public static String getJti(String token) {
		if (StrUtil.isBlank(token)) {
			return null;
		}
		try {
			String field = StrUtil.getFieldValue(token, "\\.", 1);
			String payload = new String(Base64.getDecoder().decode(field));
			return JsonUtil.parseAsTree(payload).get("jti").asText();
		} catch (Exception e) {
			log.info("get jti from token error ", e);
		}
		return null;
	}


	private static PrivateKey getPrivateKeyFromBase64EncodedStr(String key) {
		try {
			byte[] keyBytes = Base64.getDecoder().decode(key);
			PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			return keyFactory.generatePrivate(spec);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private static PublicKey getPublicKeyFromBase64EncodedStr(String key) {
		try {
			byte[] keyBytes = Base64.getDecoder().decode(key);
			X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			return keyFactory.generatePublic(spec);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void main(String[] args) {
		String publicKeyBase64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt8JHDNFgbKved7Tw1QGfrTsnX9k2aDR28GnsjbZbHLdTZd6Zpa2BMQsTr8dGdx4Gp/SXONstz/EPlDp+JHuk9YTXpFcJMlW5fEf9CFDjAV+u35QdSn5g/ZAG4PRAWLODhz4aUatKF7JtWvhKXqRb9srIbSqb2BSZyNe5iqWCkqL7vpb4qNpm5cXdlPKTUrUEpJR/Al+8Ct8Gq9g1f60lHo6zm3QFmMilkulgm7PhWLHgDNNsjlj0jcs10LwOQOjXXXZ283kTBl3CKracvEuTZ5MhVoCSr/pFdL9EtQHOf/CmssLaG9bOx1+Wu5EREcAOs3GpF+QXU2ukfW5ESW4xNQIDAQAB";
		String privateKeyBase64 = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3wkcM0WBsq953tPDVAZ+tOydf2TZoNHbwaeyNtlsct1Nl3pmlrYExCxOvx0Z3Hgan9Jc42y3P8Q+UOn4ke6T1hNekVwkyVbl8R/0IUOMBX67flB1KfmD9kAbg9EBYs4OHPhpRq0oXsm1a+EpepFv2yshtKpvYFJnI17mKpYKSovu+lvio2mblxd2U8pNStQSklH8CX7wK3war2DV/rSUejrObdAWYyKWS6WCbs+FYseAM02yOWPSNyzXQvA5A6NdddnbzeRMGXcIqtpy8S5NnkyFWgJKv+kV0v0S1Ac5/8Kaywtob1s7HX5a7kRERwA6zcakX5BdTa6R9bkRJbjE1AgMBAAECggEAOPfYag9hKt+PjSIBxa+GrsBXgR4IgX6+XuMfzIE7tufYyYycqqHjGu65sDWB0Xz0CE7KMPy2M7CiqUwG/k18wwbniZHYDoxIAFEbD2BwN+VPhA3ZlNYgQIvNriDa8ATbImrs4uPNCRKuV1UBTVz8C8MrJrjebrtYP80tPt/33nh0S6Lvy9rjoDZ+asEaBNN+tOddKbKHK72vnzCd4z4T+LpV+Cv2sxHTveXmu6uCOyLh9KPQOiWND+bNWm2ZbTQL4x47cSrKceP0Eg71hYWkYg5YuZsfAv2+lJrLz+vjoo8Zi9EYvnzaHKxhLlhZXhv3gnNqAisWsMbv7H2l3wR5owKBgQD0faLpRckeUpjay9Zo5f+ffAXrSEJD3PsEhQaCfyN8Kkuug8dTjUIxjTmllarQRhQ2psiKUuQQvppl8Ib0tM+5uasg8jLJ+LSn9hy2u0rZZRneatkXSHzN+o7qa1RLyLh+GytxGL+qAQco1Rx7wfuNGNlrR86iiLiBsj0KxZh00wKBgQDAaMKTFaUGVeaQEoEwWpVVh4jf7k7gKDwtr6VDshjWTC/VfVtz1QyDVatWey4+gaOdMft5wpNCbfCBPIjKaMfdG2KsariHJmob+cJHxg7fC3eYPE1kB6bGWuRgOdfnCy0lFYhfEdMqYAo/j5wU3JZGhHBH+UJiGfbvEL/iIc8c1wKBgBv311AKHKSsXDIDSWa8kM5kuKSeon3y5IAy23jIJH4KtJelGYM0UJMsjaCki2LB8YQC9kPkkZRcyXrJ1hZeLLkZRTtCmY7DNZ9VJxm8OSa/SwNHg8gHEcfaVsj4OC9nKbu7Te/lHd/LuE7yegyqNyV/5zc/gqLTMxAbHKKD0hnjAoGBALkgEpYSLUV7QOs94q7zeNtiHH/YemuSStRqO0h3Aw6tHxj6FBPO2MtEkiYTPttFQHbusFbmr2kq/kxaQgL97c7/mmDqDcF7ZHymQhdsqMJR9i+zVYL5Ga2fpjwva1wRlM4peZx/Of17imC9fB9WXgBbTOzzFrvs33oFU5nvjabhAoGBAODfTXpZgsa3HQrgyKeOKScASdkmnAkYItt/KRAP+J+XwLhink9g+dG4pAR5PZ2KHsLP1cx1IkzVnfq0XYJAbnswheKL3t6AqMHTHrDbsR65d6QUcJM5Z4KBQGd3+xldgqk7M2i/7e8i3KOwfhGMQcHcc5QoTUVm6reoP5SmCapC";

		final JwtTool jwtTool = new JwtTool(1000L, privateKeyBase64, publicKeyBase64);
		final String token = jwtTool.geneToken(1232);
		System.out.println(jwtTool.parseToken(token));
		System.out.println(token);
		System.out.println(getJti(token));
	}
}